/**
16.10 运行时反射
scala 编译器会将 scala 代码编译成 JVM 字节码，编译过程中会擦除 scala
特有的一些类型信息，在 scala-2.10 以前，只能在 scala 中利用 java 的反射机
制，但是通过 java 反射机制得到的是只是擦除后的类型信息，并不包括 scala
的一些特定类型信息。从 scala-2.10 起， scala 实现了自己的反射机制，我们
可以通过 scala 的反射机制得到 scala 的类型信息。
给定类型或者对象实例，通过 scala 运行时反射，可以做到：
1）获取运行时类型信息；
2）通过类型信息实例化新对象；
3）访问或调用对象的方法和属性等。
16.10.1获取运行时类型信息
scala 运行时类型信息是保存在 TypeTag 对象中，编译器在编译过程中将
类型信息保存到 TypeTag 中，并将其携带到运行期。我们可以通过 typeTag
方法获取 TypeTag 类型信息

  在scala对应的是
反射
scala. reflect. api. Mirrors#InstanceMirror 实例的指代
scala. reflect. api. Mirrors#MethodMirror method指代
scala. reflect. api. Mirrors#FieldMirror field指代
scala. reflect. api. Mirrors#ClassMirror 当前实例的calss类的声明的指代
scala. reflect. api. Mirrors#Modul eMirror package包moudle的指代

  */

case class Employee(name: String){
    def showName = print(">>>>" +name)
}


object  Main extends App{
    import scala.reflect.runtime.universe._      // for runtime reflection
    def getTypeTag[T:TypeTag](a:T) = typeTag[T]
    val emp = Employee("China")
    val classType = getTypeTag(emp).tpe

    //val classType = typeOf[Employee]
    //获取运行期镜像
    val runtimeMirrorRef = runtimeMirror(getClass.getClassLoader)


    // 获取类镜像，类似于java中的Class<?>
    val classMirrorRef = runtimeMirrorRef.reflectClass(classType.typeSymbol.asClass)

    //获取class 里面定义的构造器方法
    val methodConstructorRef = classType.decl(termNames.CONSTRUCTOR).asMethod

    //获取类实例中定义的构造函数引用
    val constructorRef = classMirrorRef.reflectConstructor(methodConstructorRef)

    //构造对象
    val employee = constructorRef("Mike")

    //获取name属性标识
    val nameTermSymb = classType.decl(TermName("name")).asTerm

    //获取showName方法标识
    val showNameTermSymb = classType.decl(TermName("showName")).asMethod

    //获取运行时实例引用
    val instanceMirrorRef = runtimeMirrorRef.reflect(employee)

    //获取运行时的属性引用
    val fieldMirrorRef = instanceMirrorRef.reflectField(nameTermSymb)

    //获取运行时的方法引用
    val methodMirrorRef = instanceMirrorRef.reflectMethod(showNameTermSymb)

    //读取属性
    println(fieldMirrorRef.get)
    //调用方法
    methodMirrorRef.apply()
}